﻿using System;
using System.Linq;
using System.Web.Mvc;

namespace Freedom.Framework.Mvc.View
{
    public class EnhanceRazorViewEngine : BuildManagerViewEngine
    {
        internal static readonly string ViewStartFileName = "_ViewStart";

        public EnhanceRazorViewEngine()
            : this(null)
        {
        }

        public EnhanceRazorViewEngine(IViewPageActivator viewPageActivator)
            : base(viewPageActivator)
        {
            AreaViewLocationFormats = new[] {
                "~/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Areas/{2}/Views/{1}/{0}.vbhtml",
                "~/Areas/{2}/Views/Shared/{0}.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.vbhtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/{1}/{0}.vbhtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/Shared/{0}.cshtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/Shared/{0}.vbhtml"
            };
            AreaMasterLocationFormats = new[] {
                "~/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Areas/{2}/Views/{1}/{0}.vbhtml",
                "~/Areas/{2}/Views/Shared/{0}.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.vbhtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/{1}/{0}.vbhtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/Shared/{0}.cshtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/Shared/{0}.vbhtml"
            };
            AreaPartialViewLocationFormats = new[] {
                "~/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Areas/{2}/Views/{1}/{0}.vbhtml",
                "~/Areas/{2}/Views/Shared/{0}.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.vbhtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/{1}/{0}.vbhtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/Shared/{0}.cshtml",
                "~/Modules/{ModuleName}/Areas/{2}/Views/Shared/{0}.vbhtml"
            };

            ViewLocationFormats = new[] {
                "~/Views/{1}/{0}.cshtml",
                "~/Views/{1}/{0}.vbhtml",
                "~/Views/Shared/{0}.cshtml",
                "~/Views/Shared/{0}.vbhtml",
                "~/Modules/{ModuleName}/Views/{1}/{0}.cshtml",
                "~/Modules/{ModuleName}/Views/{1}/{0}.vbhtml",
                "~/Modules/{ModuleName}/Views/Shared/{0}.cshtml",
                "~/Modules/{ModuleName}/Views/Shared/{0}.vbhtml"
            };
            MasterLocationFormats = new[] {
                "~/Views/{1}/{0}.cshtml",
                "~/Views/{1}/{0}.vbhtml",
                "~/Views/Shared/{0}.cshtml",
                "~/Views/Shared/{0}.vbhtml",
                "~/Modules/{ModuleName}/Views/{1}/{0}.cshtml",
                "~/Modules/{ModuleName}/Views/{1}/{0}.vbhtml",
                "~/Modules/{ModuleName}/Views/Shared/{0}.cshtml",
                "~/Modules/{ModuleName}/Views/Shared/{0}.vbhtml"
            };
            PartialViewLocationFormats = new[] {
                "~/Views/{1}/{0}.cshtml",
                "~/Views/{1}/{0}.vbhtml",
                "~/Views/Shared/{0}.cshtml",
                "~/Views/Shared/{0}.vbhtml",
                "~/Modules/{ModuleName}/Views/{1}/{0}.cshtml",
                "~/Modules/{ModuleName}/Views/{1}/{0}.vbhtml",
                "~/Modules/{ModuleName}/Views/Shared/{0}.cshtml",
                "~/Modules/{ModuleName}/Views/Shared/{0}.vbhtml"
            };

            FileExtensions = new[] {
                "cshtml",
                "vbhtml"
            };
        }


        #region Overrides of VirtualPathProviderViewEngine

        /// <summary>
        /// Creates the specified partial view by using the specified controller context.
        /// </summary>
        /// <returns>
        /// A reference to the partial view.
        /// </returns>
        /// <param name="controllerContext">The controller context.</param><param name="partialPath">The partial path for the new partial view.</param>
        protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
        {
            return new RazorView(controllerContext, partialPath,
                                 layoutPath: null, runViewStartPages: false, viewStartFileExtensions: FileExtensions, viewPageActivator: ViewPageActivator);
        }

        /// <summary>
        /// Creates the specified view by using the controller context, path of the view, and path of the master view.
        /// </summary>
        /// <returns>
        /// A reference to the view.
        /// </returns>
        /// <param name="controllerContext">The controller context.</param><param name="viewPath">The path of the view.</param><param name="masterPath">The path of the master view.</param>
        protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
        {

            var view = new RazorView(controllerContext, viewPath,
                                     layoutPath: masterPath, runViewStartPages: true, viewStartFileExtensions: FileExtensions, viewPageActivator: ViewPageActivator);
            return view;
        }

        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            var moduleName = Utils.GetAreaName(controllerContext.RouteData);
            try
            {
                AddModuleInLocationFormat(moduleName);
                return base.FindView(controllerContext, viewName, masterName, useCache);
            }
            finally
            {
                RemoveModuleInLocationFormat(moduleName);
            }
        }

        public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            var moduleName = Utils.GetAreaName(controllerContext.RouteData);
            try
            {
                AddModuleInLocationFormat(moduleName);
                return base.FindPartialView(controllerContext, partialViewName, useCache);
            }
            catch (Exception)
            {
                RemoveModuleInLocationFormat(moduleName);
                return base.FindPartialView(controllerContext, partialViewName, useCache);
            }
            finally
            {
                RemoveModuleInLocationFormat(moduleName);
            }
        }

        private void AddModuleInLocationFormat(string routeName)
        {
            Func<string, string> addModuleName = location => location.Replace("{ModuleName}", routeName);

            AreaViewLocationFormats = AreaViewLocationFormats.Select(addModuleName).ToArray();
            AreaMasterLocationFormats = AreaMasterLocationFormats.Select(addModuleName).ToArray();
            AreaPartialViewLocationFormats = AreaPartialViewLocationFormats.Select(addModuleName).ToArray();
            ViewLocationFormats = ViewLocationFormats.Select(addModuleName).ToArray();
            MasterLocationFormats = MasterLocationFormats.Select(addModuleName).ToArray();
            PartialViewLocationFormats = PartialViewLocationFormats.Select(addModuleName).ToArray();
        }

        private void RemoveModuleInLocationFormat(string routeName)
        {
            Func<string, string> addModuleName = location => location.Replace(string.Format("/Modules/{0}", routeName), "/Modules/{ModuleName}");

            AreaViewLocationFormats = AreaViewLocationFormats.Select(addModuleName).ToArray();
            AreaMasterLocationFormats = AreaMasterLocationFormats.Select(addModuleName).ToArray();
            AreaPartialViewLocationFormats = AreaPartialViewLocationFormats.Select(addModuleName).ToArray();
            ViewLocationFormats = ViewLocationFormats.Select(addModuleName).ToArray();
            MasterLocationFormats = MasterLocationFormats.Select(addModuleName).ToArray();
            PartialViewLocationFormats = PartialViewLocationFormats.Select(addModuleName).ToArray();
        }

        #endregion
    }
}
